/***************************************************************************************************
 *Copyright(C),2010-2017,Sumscope
 *FileName	:  DictTable.h
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2017/04/01
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#pragma once

#include <core/FileSys.h>
#include <core/strings/JsonWideWriter.h>
#include <core/strings/string_convert.h>
#include <string.h>

#include <QFile>
#include <bitset>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "HTable/src/HTable.h"
#include "HTable/src/TableColumn.h"
#include "HTable/src/TableCommon.h"
#include "qbprotocol/include/rdProto.h"

#ifndef BASSERT
#define BASSERT(smt) Q_ASSERT(smt)
#endif
#ifndef BASSERTIF
#define BASSERTIF  //
#endif

using qb::base::CConvertFast;
using qb::base::JsonWideWriter;
using qb::base::WideString;
namespace qb {
namespace ht {
typedef char tName32[32];
typedef char tName64[64];

/*
QB收到的字典数据包中的数据类型编码,目前只用到int32,string,double,time32四种类型
*/
enum kPackDateType {
  kPackDateTypeNone = 0,
  kPackDateTypeInt8 = 1,
  kPackDateTypeUInt8 = 2,
  kPackDateTypeInt16 = 3,
  kPackDateTypeUInt16 = 4,
  kPackDateTypeInt32 = 5,
  kPackDateTypeUint32 = 6,
  kPackDateTypeInt64 = 7,
  kPackDateTypeUInt64 = 8,
  kPackDateTypeBool = 9,
  kPackDateTypeString = 10,
  kPackDateTypeFloat = 11,
  kPackDateTypeDouble = 12,
  kPackDateTypeRaw = 13,
  kPackDateTypeTime32 = 14,  // 等同于time_t，精度秒
  kPackDateTypeTime64 = 15,  // 等同于time_t + 毫秒，精度毫秒
  kPackDateTypeCount = 16
};

enum kDictType {
  kDictTypeBondList = 0,            // 债券列表
  kDictTypeBondPrimaryInfo,         // 债券发行信息1
  kDictTypeBondAdditionInfo,        // 增续发
  kDictTypeBondOptionInfo,          // 债券行权信息
  kDictTypeBondAuctionInfo,         // 债券发行信息2
  kDictTypeBondDict,                // 字典
  kDictTypeBondHoliday,             // 节假日
  kDictTypeMacroIndex,              // 宏观指标
  kDictTypeIssuerInfo,              // 发行人
  kDictTypeProvinceInfo,            // 省份
  kDictTypeSwsectorInfo,            // 行业
  kDictTypeSwsectorInfo2021,        // 新申万行业
  kDictTypeUnderwriter,             // 承销商
  kDictTypeFinancialCompany,        // 机构
  kDictTypeContinuousContractInfo,  // 国债期货连续合约
  kDictTypeExchangeRepo,            // 交易所回购
  kDictTypeBondCDCAuth,             // 债券对应中债权限
  kDictTypeBondCRMClause,           // 质押信息
  kDictTypeABSBond,                 // ABS
  kDictTypeMainUnderwriter,         // 主承销商
  kDictTypeBondConversion,          // 可转债
  kDictTypeBondDefaulted,           // 违约债
  kDictTypeBondKongfu,              // 中资美元债
  kDictTypeSpecialLabelInfo,        // 特殊标签信息表
  kDictTypeBondSpecialLabel,        // 特殊标签Bond表
  kDictTypeCount                    //
};
typedef _xFileUpdateReq_t DictRequest;
class UpdatePacket : public xRespFileUpdateMsgMass_t {
 public:
  kDictType type;
  bool finish;  // true表示当前字典类型更新完成，需要立即更新下一个字典类型
};
class KeyFieldMaker {
 public:
  virtual void BeforeUpdate(char* record, int size, const char* recordName,
                            const char* keyName, int keyffset, int keySize) = 0;
};

struct FieldDesc {  // 记录的每个字段描述
  kDataType type;   // 内部固有的类型编码
  int offset;       // 偏移量
  int size;         // 字节大小
  tName32 name;     // 英文字段名
  tName32 desc;     // 中文描述
  FieldDesc(kDataType t, int off, int sz, const char* n, const char* d);
};
class DictDesc {
 public:
  std::string m_name;   // 要存储的数据文件名称,纯粹文件名称
  std::string m_fname;  // 根据m_name形成的全路径文件名称
  std::string m_version;  // 版本号,等同于之前的PatchName,如QBBondListV17
  std::string m_record_name;  // 对应的原始结构体名称,只用于调试查看
  std::string m_key_name;  // 用于做键值的列的名称,等同于结构体的某个字段的名称
  std::string
      m_flag_name;  // 通常是Flag列,特殊用途,用于指示底层是删除记录,还是更新记录
  std::string m_tag_name;
  int m_record_size;  // sizeof(结构体)的值,最终存储在数据表中,会ROUND4
  int m_key_column;   // 用于做键值的列的索引,
  int m_flag_column;  // Flag列的索引
  int m_predict_size;  // 默认的文件数据量规模大小,用于评估加载进度
  int m_view_size;      // 单位为1mb
  int m_increase_size;  // 单位为1mb
  kDictType m_type;
  KeyFieldMaker*
      m_keymaker;  // 某些结构体,其键值由2-3个不相连的列的值组合而成,此类用于如何从原始列中提取出键值列
  std::vector<FieldDesc> m_fields;  // 结构体的每个字段描述,用于解码
  std::vector<FieldDesc>
      m_keyfields;  // 某些结构体,键值由2个相连的列组合而成,这个用于描述这种情况
  std::vector<int> m_export_indexes;
  int m_double_precision;
  friend class DictTable;

 public:
  static const DictDesc& GetStockDictDesc(int index);
  static const char* GetDebugKey();
  static void SetDebugKey(const char* key);
  DictDesc(const std::string& name, const std::string& ver,
           const std::string& record, int recordsize, const std::string& key,
           const std::string& flag, KeyFieldMaker* maker = 0)
      : m_type(kDictTypeCount),
        m_predict_size(1000),
        m_name(name),
        m_version(ver),
        m_record_name(record),
        m_record_size(recordsize),
        m_key_name(key),
        m_flag_name(flag),
        m_double_precision(4),
        m_key_column(-1),
        m_flag_column(-1),
        m_keymaker(maker),
        m_view_size(4),
        m_increase_size(4),
        m_fname(QString(FileSys::instance()->dataDir() + QString("/BondData/"))
                    .toStdString()) {}
  const std::string& GetKeyName() const { return m_key_name; }
  const std::string& GetVersion() const { return m_version; }
  const std::string& GetName() const { return m_name; }
  int GetViewSize() const { return m_view_size; }
  int GetIncreaseSize() const { return m_increase_size; }
  void SetViewSize(int viewSize, int increaseSize);
  void SetExportFields(const char* fields);
  const std::vector<int>& GetExportFields() const { return m_export_indexes; }

  void SetTag(const std::string& tag);
  const std::string& GetTag() const { return m_tag_name; }
  void SetDoublePrecision(int precision) { m_double_precision = precision; }
  int GetDoublePrecision() const { return m_double_precision; }
  void SetPredictSize(int size) { m_predict_size = size; }
  int GetPredictSize() const { return m_predict_size; }
  void SetType(kDictType type) { m_type = type; }
  kDictType GetType() const { return m_type; }
  void AddField(const FieldDesc& fd) { m_fields.push_back(fd); }
  void AddKeyField(const FieldDesc& fd);
  void SetKeyFieldMaker(KeyFieldMaker* keymaker) { m_keymaker = keymaker; }
  bool CheckValid() {
    m_fname += m_name;
    m_key_column = FindColumn(m_key_name.c_str());
    m_flag_column = FindColumn(m_flag_name.c_str());
    BASSERT(m_record_size > 0);
    BASSERT(m_key_column >= 0 && m_key_column < (int)m_fields.size());
    BASSERTIF(m_flag_name.size() > 0,
              m_flag_column >= 0 && m_flag_column < (int)m_fields.size());
    if (m_keyfields.size() > 0) {
      FieldDesc& keyfd = m_fields[m_key_column];
      BASSERT(keyfd.offset == m_keyfields[0].offset);
      int size_sum = m_keyfields[0].size;
      for (int i = 1; i < (int)m_keyfields.size(); i++) {
        FieldDesc& fd = m_keyfields[i];
        BASSERT(fd.type ==
                (kDataType)kDataTypeCharArray);  // 要求只能是固定长度字符串类型
        BASSERT(size_sum + keyfd.offset == fd.offset);
        size_sum += fd.size;
      }
      BASSERT(size_sum == keyfd.size);
    }
    return true;
  }
  int FindColumn(const char* name, int len = -1) const {
    len = len <= 0 ? strlen(name) : len;
    for (int i = 0; i < (int)m_fields.size(); i++) {
      const FieldDesc& fd = m_fields[i];
      if (strncmp(fd.name, name, len) == 0) return i;
    }
    return -1;
  }
  bool HasColumn(const char* name) const {
    if (FindColumn(name) >= 0) return true;
    for (int i = 0; i < (int)m_keyfields.size(); i++) {
      const FieldDesc& fd = m_keyfields[i];
      if (strcmp(fd.name, name) == 0) return true;
    }
    return false;
  }
  int GetColumnCount() const { return m_fields.size(); }

 public:  // 调试,慎用
  void PrintRecord(int index, const char* record, int length,
                   bool removed) const;
  void CompareRecord(int i1, const char* r1, int i2, const char* r2,
                     int length) const;
};

class CBufferFile {
  QFile* m_file;
  char* m_buffer;
  int m_capcity;
  int m_size;
  unsigned long m_written;

 public:
  CBufferFile(const char* fname, int capcity);
  ~CBufferFile();
  void AddLine(const char* str, int len);
};
/*
此类用于加载更新原先的BondList.cod文件所支持的数据
数据以等长记录二进制形式保存在映射文件中,格式是自检的
映射文件不删除,除非主动删除(版本升级,数据损坏...)

使用方式:
在Load之前,必须调用SetKeyColumn,SetFlagColumn,SetRecordName,SetRecordSize,AddRecordInfo
来初始化
*/
class UpdatePacket;
class MacroIndexDict;
class DictTable {
 public:
  enum { TAG = 0x73626f64 };  // sbod的二进制数字形式

  typedef unsigned int uint;
  typedef unsigned short ushort;
  struct PackHeader {   // 更新包中的数据头部
    uint tag;           // 文件开头的4个字节,0x44425253
    uint StartPos;      // 记录开始的偏移量
    uint RecordCount;   // 记录个数
    ushort FieldCount;  // 每条记录的字段个数
  };
  struct RecordState {  // 一个文件更新包中的记录状态
    RecordId rid;       // 记录id,对应与HTable中的记录id
    int seq;            // 仅用于调试:
    bool removed;       // true表示该记录被删除了
    char* record;  // 仅用于调试:只有当removed为true的时候,才有效;
  };
  typedef std::map<std::string, RecordState> RecordStateMap;
  class UpdateSet {
    RecordStateMap m_statemap;

   public:
    void Add(const char* podRecord, int keyoffset, int podSize,
             RecordState& state);
    void Reset(int* pnrecord);
    const RecordStateMap& GetStateMap() const { return m_statemap; }
    int size() const { return m_statemap.size(); }
  };
  struct TableHead {   // HTable要保存的映射文件头内容,
    int record_count;  // 记录个数,加载删除无效记录后就不变,强行约定最开始4个字节表示记录个数
    unsigned int tag;  // 固定值,如"SBOD"
    unsigned int crc;  // crc校验,对余下的内容
    tName32 name;      // 数据名称如"BondList.bod"
    tName32 version;  // 数据版本,如"BondListV17",对应后台数据库名称
    tName64 record_name;  // 结构体名称,如"BOND_LIST"

    // 每次更新一个数据包的时候,必须更新的字段
    tName64 state;  // 状态id,如"[md5-id]",从文件中获取
    int updateid;   // 一次更新的id

    int head_size;        // 头部大小
    int desc_offset;      // 描述偏移量
    int record_offset;    // 记录偏移量,以record_size边界对齐
    int desc_item_size;   // 描述结构体大小
    int desc_item_count;  // 描述结构体个数
    int record_size;      // 记录大小,==ROUND4(实际记录字节数)
    int key_column;       // 关键列,键值列,[0,desc_item_count)
    int flag_column;      // 标志列
    int del_count;        // 删除的记录个数,不做检查
    int view_size;        // 映射文件隐射视图大小,单位为1mb
    int inc_size;         // 映射文件递增大小,单位为1mb
  };
  class SequenceSet : public std::set<int> {};

 protected:
  std::string m_fname;
  TableHead m_head;                           // 内存中的文件头
  std::vector<FieldDesc> m_record_descs;      // 内存中的记录字段描述
  std::vector<FieldDesc> m_key_split_fields;  // 解码之前的字段
  std::vector<std::string>
      m_last_field_names;  // 上次解码的更新包的字段名称序列
  std::vector<FieldDesc>
      m_last_fields;  // 上次解码的更新包的字段名称对应的字段信息
  SequenceSet
      m_seqset;  // 当前字典的序号集合,只判断更新包里面的seq,忽略本地的seq
  SequenceSet m_diffset;   //
  qb::ht::Table* m_table;  // 数据表
  int m_key_column;        // 主键列
  int m_flag_column;  // 标志位列,用于删除,只能是bool,true表示删除
  int m_viewsize, m_increase_size;
  UpdateSet m_update_map;  // 当前一个更新包中更新的记录集合
  char* m_pod_record;      // 内部使用的一条pod记录的内存
  int m_pod_record_size;   // pod记录的字节大小
  KeyFieldMaker* m_keyfield_maker;
  MacroIndexDict* m_macroindex_dict;
  qb::base::CConvertFast m_converter;
  kDictType m_type;
  std::bitset<32>
      m_dict_flag;  // 用于是否要将数据写入到日志中，每一个bit表示一种字典类型
  char m_key_linkchar;  // 默认|
  int m_seq_col, m_seq_offset;

 public:
  explicit DictTable(const DictDesc& dd);
  ~DictTable();

  kDictType GetType() const { return m_type; }
  void SetKeyfieldMaker(KeyFieldMaker* maker) { m_keyfield_maker = maker; }
  void SetKeyColumn(int col) { m_key_column = col; }
  void SetFlagColumn(int col) { m_flag_column = col; }
  int GetColumnByName(const char* name) const;
  void SetKeySplitColumns(std::vector<FieldDesc>& fields);
  void SetRecordName(const std::string& record);
  void SetRecordSize(int size) { m_head.record_size = ROUND4(size); }
  void AddRecordInfo(int type, int offset, int size, const char* name,
                     const char* desc);
  void AddRecordInfo(const FieldDesc& fd);
  const char* GetMd5State() const { return m_head.state; }
  const char* GetName() const { return m_head.name; }
  int GetUpdateId() const { return m_head.updateid; }

  bool Load(std::string& error);
  void Unload();
  bool Reload();
  bool Export(const char* fullFileName);
  bool GetSequences(std::vector<int>& sequences);
  bool GetSequenceInfo(int& col, int& offset);

  void RecreateFile();
  RecordId Remove(const char* key);
  RecordId Update(const char* record, int size);
  void UpdateMacroIndex(bool sync);
  int UpdatePack(UpdatePacket* raw, bool all);
  int UpdateModifyPack(const char* key, const char* fieldName,
                       const char* shortName);
  void UpdateHead();

  int GetLastUpdateSetSize() const { return m_update_map.size(); }
  const UpdateSet& GetLastUpdateSet() const { return m_update_map; }
  void ClearUpdateSet();
  void ClearSeqSet();
  bool GetUpdatedRecord(RecordId rid, char* record, int size);

  int GetRecordLength() const { return m_table->GetRecordLength(); }
  int GetRecordCount() const { return m_table->GetRecordCount(); }
  int GetMaxRecordId() const { return m_table->GetMaxRecordId(); }
  const char* GetRecordName() const { return m_head.record_name; }
  RecordId GetRecordId(const char* key);
  void GetAllRecord(std::vector<unsigned int>& records);

  bool ReadRecord(RecordId rid, char* record, int size);
  const char* ReadRecord(RecordId rid);

  char* GetPodRecord(int& size);
  void FlushCache();

 public:
  void DumpStatus(int mode = 0);

 public:
  bool DeleteRecords(double percent, std::string& report);

 protected:
  void AddUpdateSet(char* pod_record, int pod_size, RecordId rid, bool removed,
                    int keyoffset);
  bool UpdatePodRecordImpl(char* pod_record, int pod_size, bool all);
  bool UpdatePodRecord(char* pod_record, int pod_size, bool all);
  bool GetExportFields(std::vector<FieldDesc>& fields);
  bool GetRawFields(std::vector<FieldDesc>& fields, bool withFlag = true);
  const std::vector<FieldDesc>& GetRawFieldsByNames(
      const std::vector<const char*>& names);

  void MergeKeyField(char* record, int length);
  void SplitKeyField(char* record, int length);
  bool CheckLoadParams(std::string& error);
  bool CheckFileHead(std::string& error);
  bool CheckFileHeadDirectly(const std::string& fname, std::string& error);
  bool CheckValid(TableHead* head, FieldDesc* pdesc, std::string& error);
  bool CheckValue(int n1, int n2, const char* name, std::string& error,
                  int field = -1);
  bool CheckValue(const char* s1, const char* s2, const char* name,
                  std::string& error, int field = -1);
  bool CheckAndLoad(std::string& error);
  bool ClearDeleteRecords();
  unsigned int GetHeadCRC(const TableHead* head);

  void MakeWritablePath(const char* name, std::string& fpath, bool& tempfile);
};
}  // namespace ht
}  // namespace qb
